#include "Frustum.h"
#include "Triangle.h"
#include <stdio.h>

using namespace geom;
Frustum::Frustum(const Point3D & viewPoint, const Vector3D & viewDir, float nearPlane, float farPlane, float horizFOV, float aspectRatio)
{

	// compute width and height of the near and far plane sections
	float tang = (float)tan(M_PI/180.0f * horizFOV  * 0.5f) ;
	float nh = nearPlane * tang;	// near plane height
	float nw = nh * aspectRatio; 		// near plane width
	float fh = farPlane  * tang;  // far plane height
	float fw = fh * aspectRatio;    // far plane width

	Vector3D X,Y,Z;
	Point3D nc, fc;

	// compute the Z axis of camera
	// this axis points in the opposite direction from 
	// the looking direction
	Z = viewDir; 
	Z = Z.getNormalized();

	// X axis of camera with given "up" vector and Z axis
	X = Vector3D(0,-1,0).vectorProduct(Z);
	X = X.getNormalized();

	// the real "up" vector is the cross product of Z and X
	Y = Z.vectorProduct(X);

	// compute the centers of the near and far planes
	nc = viewPoint - Z * nearPlane;
	fc = viewPoint - Z * farPlane;

	// compute the 4 corners of the frustum on the near plane
	Point3D ntl = nc + Y * nh - X * nw;
	Point3D ntr = nc + Y * nh + X * nw;
	Point3D nbl = nc - Y * nh - X * nw;
	Point3D nbr = nc - Y * nh + X * nw;

	// compute the 4 corners of the frustum on the far plane
	Point3D ftl = fc + Y * fh - X * fw;
	Point3D ftr = fc + Y * fh + X * fw;
	Point3D fbl = fc - Y * fh - X * fw;
	Point3D fbr = fc - Y * fh + X * fw;

	// top Plane
	Triangle tTop(ntl, ntr, ftr);
	m_planes[0].m_p = ntl;
	m_planes[0].m_n = tTop.getNormal();

	// bottom Plane
	Triangle tBottom(nbr, nbl, fbl);
	m_planes[1].m_p = nbr;
	m_planes[1].m_n = tBottom.getNormal();

	// left Plane
	Triangle tLeft(nbl, ntl, ftl);
	m_planes[2].m_p = nbl;
	m_planes[2].m_n = tLeft.getNormal();

	// right Plane
	Triangle tRight(ntr, nbr, fbr);
	m_planes[3].m_p = ntr;
	m_planes[3].m_n = tRight.getNormal();

	// near plane
	Triangle tNear(ntl, nbl, ntr);
	m_planes[4].m_p = ntl;
	m_planes[4].m_n = tNear.getNormal();

	// Far plane
	Triangle tFar(ftl, ftr, fbr);
	m_planes[5].m_p = ftl;
	m_planes[5].m_n = tFar.getNormal();

}

bool Frustum::contains(const Point3D & p) const
{
	for(int i=0;i<6;++i)
		if(m_planes[i].distance(p) > 0)
			return false;

	return true;
}

bool Frustum::contains(const BSphere & s) const
{
	for(int i=0;i<5;++i) // We don't test against the far plane because of a BUG somewhere, but it's ok, because we manage this with a distance check
		if(m_planes[i].distance(s.center()) > s.radius())
			return false;

	return true;
}

